<?php defined('SYSPATH') OR die('No direct access allowed.');

class WB_Form {
	
	protected $fields = array();
	private $initialized = FALSE;
	
	public function __construct() {}
	
	public function initialize($db_table, $primary_key='id', $primary_params=array())
	{
		if ($primary_key) 
		{
			$primary_params['class'] = 'PrimaryKey';
			$primary_params['db_select'] = isset($primary_params['db_select']) ? $primary_params['db_select'] : $db_table.'.'.$primary_key;
			$primary_params['db_table'] = isset($primary_params['db_table']) ? $primary_params['db_table'] : $db_table;
			$this->fields[$primary_key] = WB_Form::factory($primary_key, $primary_params);
		}
		$this->initialized = TRUE;
	}

	/**
	 * Creates and returns a new plugin.
	 *
	 * @chainable
	 * @param   string  plugin name
	 * @param   mixed   plugin parameters
	 * @return  WB_Form
	 */
	public static function factory($name, $params)
	{
		// Get value for setting it after construction
		if (isset($params['value']))
		{
			$value = $params['value'];
			unset($params['value']);
		}
		else $value = null;
		
		// Extract class name and unset from parameters
		if (isset($params['class']) && ! $params['class']!='PrimaryKey')
		{
			$plugin = $params['class'];
			unset($params['class']);
		}
		else $plugin = 'TextBox';
		
		// Set class name and unset from parameters
		if (file_exists(APPPATH.'plugins/'.strtolower($plugin).'.php')) 
		{
			require_once APPPATH.'plugins/'.strtolower($plugin).'.php';
		}
		else
		{
			include_once APPPATH.'plugins/textbox.php';
			$plugin = 'TextBox';
		}
		
		$class = $plugin.'_Plugin';
		$obj = new $class($name, $params);
		
		if ($value !== null) $obj->set_value($value, 'init');
		
		return $obj;
	}
	
	/**
	 * Builds a form from an array
	 * 
	 * @param array $form_array
	 * @param string $db_table
	 * @return void
	 */
	public function build($form_array, $db_table)
	{
		if ( ! $this->initialized)
		{
			$this->initialize($db_table);
		}
		foreach ($form_array as $name=>$params)
		{
			if ( ! is_array($params) || empty($name)) throw new Kohana_Exception ('admin.invalid_form_building');
			$params['db_table'] = isset($params['db_table']) ? $params['db_table'] : $db_table;
			$this->fields[$name] = WB_Form::factory($name, $params);
		}
	}
	
	
	
	/**
	 * Sets any values for each element
	 * 
	 * @param string $setter function name we'll call with the values
	 * @param array|null $values for passing to each form items
	 * @param array|null $params for passing to all form items
	 * @return void
	 */
	public function set_all($setter, $values, $params=null)
	{
		if (isset($this->name) && isset($values[$this->name]) && is_callable(array($this, $setter))) $this->$setter($values[$this->name], $params);
		foreach ($this->fields as &$field) $field->set_all($setter, $values, $params);
	}
	
	/**
	 * Collects any values from each element
	 * 
	 * @param string $data either a function name or the variable name we need to collect
	 * @param array|null $sel form elements to collect data from
	 * @param array|null $values for passing to the form item
	 * @param boolean $nonull unsets null values
	 * @return array
	 */
	public function collect_data($data, $sel=null, $values=null, $nonull=TRUE)
	{
		$array = array();
		if ( isset($this->name) && ( ! is_array($sel) || in_array($this->name, $sel)))
		{
			$arg = isset($values[$this->name]) ? $values[$this->name] : null;
			$array[$this->name] = 
					is_callable(array($this, $data)) ?
					$this->$data($arg) : 
					(
						isset($this->$data) ?
								$this->$data :
								null
					);
			if ($nonull && is_null($array[$this->name])) unset($array[$this->name]);
		}
		foreach ($this->fields as $field) $array = array_merge($array, $field->collect_data($data, $sel, $values, $nonull));
		
		return $array;
	}
	
	
	/**
	 * Renders a form
	 * 
	 * @return string
	 */
	public function render()
	{
		$classes = $this->collect_data('get_class');
		$str = in_array('File_Plugin', $classes) || in_array('Image_Plugin', $classes) ? 
					form::open_multipart() :
					form::open();
		foreach ($this->fields as $field) $str .= $field->render_item();
		$str .= '<p class="submit">';
		$str .= form::submit(empty($this->attributes) ? 'submit' : $this->attributes, Kohana::lang('admin.submit'));
		$str .= html::anchor(Kohana::instance()->uri->string(), Kohana::lang('admin.cancel'));
		$str .= '<p>';
		$str .= form::close();
		
		return $str;
	}

	/**
	 * Magically converts menu object to string.
	 *
	 * @return  string
	 */
	public function __toString()
	{
		try
		{
			return $this->render();
		}
		catch (Exception $e)
		{
			// Display the exception using its internal __toString method
			return (string) $e;
		}
	}
	
	/**
	 * Returns a string for deubugging purposes only
	 * 
	 * @return string
	 */
	public function debug()
	{
		$str = '<h1>'.$this->name.' ('.$this->label.")</h1>";
		$str .= '<pre>';
		$str .= print_r($this, TRUE);
		$str .= "</pre>\n";
		return $str;
	}
}

//End of WB_Form Library